package cn.tedu._07springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Aspect注解:
 *  1.标识一个切面类;
 *  2.当调用目标方法时,会自动调用切面类中的方法;
 */
@Aspect
@Component
public class LogAspect {
    /**
     * Pointcut注解:定义切入点表达式的方法;
     */
    //@Pointcut("execution(public int cn.tedu._07springaop.aop.CalculatorImpl.*(..))")
    //拦截指定的多个请求;
    //@Pointcut("execution(public int cn.tedu._07springaop.aop.CalculatorImpl.add(..)) || execution(public int cn.tedu._07springaop.aop.CalculatorImpl.sub(..))")
    //粗粒度:bean()
    //@Pointcut("bean(calculatorImpl)")
    //粗粒度:within()
    @Pointcut("within(cn.tedu._07springaop.aop.CalculatorImpl)")
    public void doTime(){}

    /**
     * Before注解:前置通知,在目标方法开始执行之前执行;
     * @param joinPoint 连接点参数[被代理的目标方法对象]
     */
    @Before(value = "doTime()")
    public void beforeMethod(JoinPoint joinPoint){
        //获取目标方法的方法名和方法参数;
        String name = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("[前置通知]" + name + "方法开始了,参数为:" + args);
    }

    /**
     * After注解:
     *   1.后置通知,在目标方法彻底结束后[正常结束|异常结束]执行;
     *   2.后置通知没有权限获取到目标方法的返回值;
     */
    @After(value = "doTime()")
    public void afterMethod(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("[后置通知]" + name + "方法结束了");
    }

    /**
     * AfterReturning注解:
     *   1.返回通知,在目标方法正常返回之后执行;
     *   2.返回通知有权限获取到目标方法的返回值;
     */
    @AfterReturning(value = "doTime()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("[返回通知]" + name + "方法正常返回,结果为:" + result);
    }

    /**
     * AfterThrowing注解:
     *   1.异常通知,目标方法抛出异常时执行;
     *   2.异常通知有权限获取到目标方法抛出的异常对象;
     */
    @AfterThrowing(value = "doTime()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        System.out.println("[异常通知]" + name + "方法抛出异常,异常信息为:" + ex.getMessage());
    }

    /**
     * Around注解:
     *   1.环绕通知,等价于4个注解的组合:Before、After、AfterReturning、AfterThrowing;
     *   2.环绕通知有权限获取目标方法的返回值;
     *   3.环绕通知有权限获取异常对象;
     *   4.环绕通知可以执行目标方法;
     *   5.环绕通知通过通过 try ... catch ... finally 语句来处理目标方法;
     */
    @Around(value = "doTime()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        /*
            try {
                1.前置通知;
                2.执行目标方法;
                3.返回通知;
            }catch (Throwable ex) {
                4.异常通知;
            }finally {
                5.后置通知;
            }
         */
        try {
            System.out.println("[环绕通知]:前置通知");
            result = joinPoint.proceed();
            System.out.println("[环绕通知]:返回通知");
        } catch (Throwable e) {
            System.out.println("[环绕通知]:异常通知");
        } finally {
            System.out.println("[环绕通知]:后置通知");
        }
        return result;
    }

}

















